#include "soft_timer.h"

#define SFT_TMR_MAX_PERIOD (0xffffffff >> 1)

//基础定时器结构
struct sft_tmr_t {
    uint32_t timeout;
    uint32_t period;
    uint32_t auto_reload;
    sft_tmr_callback_t tmr_callBack;
    void *para;

    struct sft_tmr_t *next;
};

static struct sft_tmr_t head = {0, 0, 0, NULL, NULL, &head};
static volatile uint32_t cnt_tick = 0;

static void *default_malloc(uint32_t sz)
{
    return malloc(sz);
}

static void default_free(void *ptr)
{
    free(ptr);
}

static struct memory_management_hooks_t global_hooks = {
    default_malloc,
    default_free
};

void sft_tmr_init_hooks(struct memory_management_hooks_t *hooks)
{
    if(hooks)
    {
        global_hooks.malloc_fn = hooks->malloc_fn;
        global_hooks.free_fn = hooks->free_fn;
    }
}

int32_t sft_tmr_create(struct sft_tmr_t **sft_tmr, uint32_t period, uint32_t auto_reload, sft_tmr_callback_t tmr_callBack, void *para)
{
    struct sft_tmr_t *sft_tmr_new;

    sft_tmr_new = (struct sft_tmr_t *)global_hooks.malloc_fn(sizeof(*sft_tmr_new));
    if(!sft_tmr_new)
    {
        return -1;
    }

    sft_tmr_new->period         = (period < SFT_TMR_MAX_PERIOD ? period : SFT_TMR_MAX_PERIOD - 1);
    sft_tmr_new->auto_reload    = auto_reload;
    sft_tmr_new->tmr_callBack   = tmr_callBack;
    sft_tmr_new->para           = para;

    *sft_tmr = sft_tmr_new;

    return 0;
}

void sft_tmr_delete(struct sft_tmr_t *sft_tmr)
{
    if(!sft_tmr) { return ; }

    struct sft_tmr_t *pos, *prev;

    prev = &head;
    pos = head.next;
    for(;pos != &head;pos = pos->next)
    {
        if(pos == sft_tmr)
        {
            break;
        }
        prev = pos;
    }
    if(pos == sft_tmr)
    {
        prev->next = prev->next->next;
        global_hooks.free_fn(sft_tmr);
    }
}

void sft_tmr_change_period(struct sft_tmr_t *sft_tmr, uint32_t period)
{
    if(!sft_tmr) { return ; }

    sft_tmr->period = (period < SFT_TMR_MAX_PERIOD ? period : SFT_TMR_MAX_PERIOD - 1);
}

uint32_t sft_tmr_get_period(struct sft_tmr_t *sft_tmr)
{
    if(!sft_tmr) { return 0; }

    return sft_tmr->period;
}

void sft_tmr_redirect_callback(struct sft_tmr_t *sft_tmr, sft_tmr_callback_t tmr_callBack, void *para)
{
    if(!sft_tmr) { return ; }

    sft_tmr->tmr_callBack = tmr_callBack;
    sft_tmr->para = para;
}

void sft_tmr_start(struct sft_tmr_t *sft_tmr, uint32_t triger_now)
{
    if(!sft_tmr) { return ; }

    struct sft_tmr_t *pos, *prev;

    sft_tmr->timeout = cnt_tick + (triger_now ? 0 : sft_tmr->period);

    //如果定时器已经开启，则更新下次到期时间
    sft_tmr_pause(sft_tmr);

    prev = &head;
    pos = head.next;
    for(;pos != &head;pos = pos->next)
    {
        if(pos->timeout > sft_tmr->timeout)
        {
            break;
        }
        prev = pos;
    }
    if((pos->timeout > sft_tmr->timeout) || (pos == &head))
    {
        sft_tmr->next = pos;
        prev->next = sft_tmr;
    }
}

void sft_tmr_pause(struct sft_tmr_t *sft_tmr)
{
    if(!sft_tmr) { return ; }

    struct sft_tmr_t *pos, *prev;

    prev = &head;
    pos = head.next;
    for(;pos != &head;pos = pos->next)
    {
        if(pos == sft_tmr)
        {
            break;
        }
        prev = pos;
    }
    if(pos == sft_tmr)
    {
        prev->next = prev->next->next;
    }
}

void sft_tmr_inc_tick(void)
{
    ++cnt_tick;
}

void sft_tmr_loop(void)
{
    struct sft_tmr_t *tmr, *next;

    /*如果存在超时相同的定时器，则继续向后找*/

    tmr = head.next;
    while(tmr != &head)
    {
        next = tmr->next;

        if((cnt_tick - tmr->timeout) < SFT_TMR_MAX_PERIOD)
        {
            //从定时器链中取出此定时器
            sft_tmr_pause(tmr);
            //如果是周期定时器，则重新添加此定时器到合适的位置
            if(tmr->auto_reload)
            {
                sft_tmr_start(tmr, 0);
            }

            //执行用户回调
            if(tmr->tmr_callBack)
            {
                tmr->tmr_callBack(tmr->para);
            }
        }
        else
        {
            break;
        }

        tmr = next;
    }
}

#include <stdio.h>
void tmr_list_for_each(void)
{
    struct sft_tmr_t *pos;

    pos = head.next;
    for(;pos != &head;pos = pos->next)
    {
        printf("%u->", pos->timeout);
    }printf("null\n-----------------------\n");
}
